/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author Karthik T
 */
public class ConnectionManager implements IMainGame,Runnable{

    private BlueToothManager btMan;
    private int mode;
    private Thread thread;
    MobilePong parent;

    //TODO others may be needed for screen size etc later.. low prio
    private final char ballDet='b';
    private final char ballCollide ='B';
    private final char paddleDet='P';
    private final char pingTest='L';
    private final char gameDet='G';//score and speed details - for now matchend is handling
    private final char gameStart ='S';
    private final char gameEnd ='E';
    private final char matchStart = 's';
    private final char matchEnd ='e';
    private final char rotateCCW = 'R';
    private final char rotateCW = 'r';
    private final char rotateN = 'N';

    boolean reset = false;
    private long pingTime;

    public ConnectionManager(MobilePong parent,int mode) {
        this.mode = mode;
        this.parent=parent;
        if (mode==MainGame.HOST)runServer();
        else runClient();
        thread = new Thread(this);
    }

    public void updateIndicator(String msg, boolean isIncrease){parent.updateIndicator(msg, isIncrease);}

    public void start(){
        thread.start();
    }

    private void runServer() {
        btMan = new BlueToothManager(this, true);

    }

    private void runClient() {
        btMan = new BlueToothManager(this, false);
    }

    public void msgProcessor(byte[] pkt, int length) {
        message msg=new message(pkt);
        switch(msg.type){
            case ballDet:       receiveBallDet(msg);break;
            case ballCollide:   receiveBallCollide(msg);break;
            case paddleDet:     receivePaddleDet(msg);break;
            case pingTest:      receivePing(msg);break;
            case gameStart:     receiveGameStart(msg);break;
            //case gameEnd:       receiveGameEnd(msg,length);break;
            case matchStart:    receiveMatchStart(msg);break;
            case matchEnd:      receiveMatchEnd(msg);break;
            case rotateCCW:     recieveRotateCCW(msg);break;
            case rotateCW:      recieveRotateCW(msg);break;
            case rotateN:       recieveRotateN(msg);break;
            default:System.out.println("Invalid Message"+msg.type);
        }
    }

    public void btInitComplete(boolean isBlueToothReady) {
        if(isBlueToothReady)parent.rdyToStart(mode);
        if (isBlueToothReady == false) {
            btMan.setIsGameOver(true);
            btMan.closeStream();
            parent.connectFailed();
        }
    }

    public void run() {
        while(!reset){
            //System.out.println("Thread run");
            sendPing();
            sendPaddleDet();
            if(mode==MainGame.HOST)sendBallDet();
            try {                
                thread.sleep(500);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }
    
   private void sendPing(){
        message msg=new message(pingTest);
        msg.packInt(0);
        pingTime=System.currentTimeMillis();
        msg.packInt((int)pingTime%10000);
        btMan.sendMsg(msg.getMsg());
    }

    private void receivePing(message msg){
        if(msg.unpackInt()==1){
            msg=new message(msg.getMsg());
            msg.packInt(2);
            btMan.sendMsg(msg.getMsg());
        }else{
            long latency = System.currentTimeMillis()%10000-msg.unpackInt();
            if(latency<0)latency+=10000;
            System.out.println("Current Latency is - "+latency+"ms");
        }
    }

    public void sendPaddleDet() {
        message msg=new message(paddleDet);
        msg.packInt(MainGame.local_paddle.getPosX());
        msg.packInt(MainGame.local_paddle.getVelX());
        btMan.sendMsg(msg.getMsg());
        //System.out.println("Sending Paddle Det - "+MainGame.local_paddle.getPosX()+MainGame.local_paddle.getVelX());
    }  

    private void receivePaddleDet(message msg){
        MainGame.top_paddle.setPosX(parent.theGame.SCREEN_WIDTH-msg.unpackInt()-MainGame.top_paddle.paddleSprite.getWidth());
        MainGame.top_paddle.setVelX(-1*msg.unpackInt());
        //System.out.println("Receiving Paddle Det - "+MainGame.local_paddle.getPosX()+MainGame.local_paddle.getVelX());
    }

    private message packBallDet(message msg){
        msg.packInt(MainGame.Ball.getPosX());
        msg.packInt(MainGame.Ball.getPosY());
        msg.packInt(MainGame.Ball.getVelX());
        msg.packInt(MainGame.Ball.getVelY());
        msg.packInt(MainGame.Ball.getSpeed());
        return msg;
    }
    
    public void sendBallDet() {
        message msg=new message(ballDet);
        msg=packBallDet(msg);
        debugPrint("Sending Ball Det - "+MainGame.Ball.getPosX()+" "+MainGame.Ball.getPosY()+" "+MainGame.Ball.getVelX()+" "+MainGame.Ball.getVelY()+" "+MainGame.Ball.getSpeed());
        btMan.sendMsg(msg.getMsg());
    }
    
    private void receiveBallDet(message msg){
        int posX=parent.theGame.SCREEN_WIDTH-msg.unpackInt()-MainGame.Ball.ballSprite.getWidth(),posY=parent.theGame.SCREEN_HEIGHT-msg.unpackInt()-MainGame.Ball.ballSprite.getHeight();
        if(posX-MainGame.Ball.getPosX()>5||posX-MainGame.Ball.getPosX()<-5)MainGame.Ball.setPosX(posX);
        if(posX-MainGame.Ball.getPosY()>5||posX-MainGame.Ball.getPosY()<-5)MainGame.Ball.setPosY(posY);
        MainGame.Ball.setVelX(-1*msg.unpackInt());
        MainGame.Ball.setVelY(-1*msg.unpackInt());
        MainGame.Ball.setSpeed(msg.unpackInt());
        debugPrint("Receiving Ball Det - "+MainGame.Ball.getPosX()+" "+MainGame.Ball.getPosY()+" "+MainGame.Ball.getVelX()+" "+MainGame.Ball.getVelY()+" "+MainGame.Ball.getSpeed());
    }
    
    public void sendBallCollide(){        
        message msg=new message(ballCollide);
        msg=packBallDet(msg);
        btMan.sendMsg(msg.getMsg());
    }

    private void receiveBallCollide(message msg) {
        MainGame.Ball.setPosX(parent.theGame.SCREEN_WIDTH-msg.unpackInt()-MainGame.Ball.ballSprite.getWidth());
        MainGame.Ball.setPosY(parent.theGame.SCREEN_HEIGHT-msg.unpackInt()-MainGame.Ball.ballSprite.getHeight());
        MainGame.Ball.setVelX(-1*msg.unpackInt());
        MainGame.Ball.setVelY(-1*msg.unpackInt());
        MainGame.Ball.setSpeed(msg.unpackInt());
        debugPrint(MainGame.Ball.getVelX()+" "+ MainGame.Ball.getVelY()+" "+MainGame.Ball.getSpeed());
    }

    public void sendGameStart() {
        message msg=new message(gameStart);
        btMan.sendMsg(msg.getMsg());
    }

    private void receiveGameStart(message msg) {
        parent.started=true;
    }

    //TODO figure out what is gameover
    public void sendGameEnd(int winner) {
        message msg=new message(gameEnd);
        msg.packInt(winner);
        btMan.sendMsg(msg.getMsg());
    }

    private void receiveGameEnd(message msg) throws InterruptedException {
        if(msg.unpackInt()==MainGame.BOTTOM)MainGame.gameOver(false);
        else MainGame.gameOver(true);
    }

    public void sendMatchStart() {
        message msg=new message(matchStart);
        btMan.sendMsg(msg.getMsg());
    }

    private void receiveMatchStart(message msg) {
        MainGame.Ball.waiting=false;
        debugPrint("Match Started");
    }

    public void sendMatchEnd() {
        message msg=new message(matchEnd);
        msg.packInt(MainGame.local_paddle.getScore());
        msg.packInt(MainGame.top_paddle.getScore());
        btMan.sendMsg(msg.getMsg());
    }

    private void receiveMatchEnd(message msg) {
        MainGame.Ball.reset();
        MainGame.Ball.matchOver();
        MainGame.top_paddle.setScore(msg.unpackInt());
        MainGame.local_paddle.setScore(msg.unpackInt());
    }

    void reset() {
        reset=true;
        btMan.setIsGameOver(true);
        btMan.closeStream();
    }

    void sendRotateCCW() {
        message msg=new message(rotateCCW);
        btMan.sendMsg(msg.getMsg());
    }
    private void recieveRotateCCW(message msg) {
        MainGame.top_paddle.rotateCCW();
    }
    
    void sendRotateCW() {
        message msg=new message(rotateCW);
        btMan.sendMsg(msg.getMsg());
    }

    private void recieveRotateCW(message msg) {
        MainGame.top_paddle.rotateCW();
    }

    void sendRotateN() {
        message msg=new message(rotateN);
        btMan.sendMsg(msg.getMsg());
    }

    private void recieveRotateN(message msg) {
        MainGame.top_paddle.rotateN();
    }
    private void debugPrint(String msg){
        //System.out.println(msg);
    }
}
